package org.lex.explorer;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;

import javax.swing.JButton;
import javax.swing.JSplitPane;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.SwingWorker;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.lex.explorer.address.AddressBar;
import org.lex.explorer.directory.DirectoryExplorer;
import org.lex.explorer.file.FileExplorer;
import org.lex.swing.ContainerFactory;
import org.lex.swing.Swings;
import org.lex.utils.IconCache;
import org.lex.utils.Pair;
import org.lex.utils.SystemProperties;
import org.lex.utils.text.SizeFormat;

public class Explorer extends AbstractExplorer {
	private JToolBar toolBar;
	private JButton upButton;
	private JSplitPane splitPane;
	private JButton refreshButton;
	private JToggleButton treeButton;
	// ==========================
	private AddressBar addressBar = null;
	private DirectoryExplorer directoryExplorer = null;
	private FileExplorer fileExplorer = null;
	private StatusBar statusBar = null;
	private File directory = null;

	public static void main(String[] args) {
		Swings.setUseBoldMetalFont(false);
		Swings.show(new Explorer(new File("d:")), false);
	}

	public Explorer() {
		this(new File(SystemProperties.userHome()));
	}

	public Explorer(File directory) {
		super();
		this.directory = directory;
		this.initialize();
		this.hookActionListeners();
		this.updateStatusBar();
	}

	@Override
	public File getDirectory() {
		return this.directory;
	}

	@Override
	public void setDirectory(final File newDir, boolean fireEvent) {
		if (null == newDir)
			return;
		File old = this.directory;
		this.directory = newDir;
		this.getAddressBar().setDirectory(newDir, false);
		this.getDirectoryExplorer().setDirectory(newDir, false);
		this.getFileExplorer().setDirectory(newDir, false);
		this.updateStatusBar();
		if (true == fireEvent)
			this.fireDirectoryChange(old, newDir);
	}

	public AddressBar getAddressBar() {
		if (this.addressBar == null) {
			this.addressBar = new AddressBar(this.directory);
		}
		return addressBar;
	}

	public DirectoryExplorer getDirectoryExplorer() {
		if (directoryExplorer == null) {
			directoryExplorer = new DirectoryExplorer(this.directory);
		}
		return directoryExplorer;
	}

	public FileExplorer getFileExplorer() {
		if (fileExplorer == null) {
			fileExplorer = new FileExplorer(this.directory);
		}
		return fileExplorer;
	}

	public void setSplitLocation(int location) {
		this.getSplitPane().setDividerLocation(location);
	}

	private JToggleButton getTreeButton() {
		if (treeButton == null) {
			treeButton = new JToggleButton(new ToggleSplitPaneAction(getSplitPane()));
		}
		return treeButton;
	}

	private JButton getUpButton() {
		if (upButton == null) {
			upButton = new JButton();
			upButton.setToolTipText("Go Up");
			upButton.setIcon(IconCache.getIcon(IconCache.GoUp));
			upButton.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					File parent = getDirectory().getParentFile();
					if (null != parent)
						setDirectory(parent, true);
				}
			});
		}
		return upButton;
	}

	private JButton getRefreshButton() {
		if (refreshButton == null) {
			refreshButton = new JButton();
			refreshButton.setToolTipText("Refresh");
			refreshButton.setIcon(IconCache.getIcon(IconCache.Refresh));
			refreshButton.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					refresh();
				}
			});
		}
		return refreshButton;
	}

	private void updateStatusBar() {
		new SwingWorker<Void, Void>() {
			@Override
			protected Void doInBackground() throws Exception {
				Pair<String, String> p = StatisticsUtilities.generateFileStatistics(directory);
				getStatusBar().setInformation(p.first());
				getStatusBar().setSize(p.second());
				return null;
			}
		}.execute();
	}

	private void initialize() {
		BorderLayout thisLayout = new BorderLayout();
		this.setLayout(thisLayout);
		this.setPreferredSize(new java.awt.Dimension(454, 337));
		this.add(getToolBar(), BorderLayout.NORTH);
		this.add(getSplitPane(), BorderLayout.CENTER);
		this.add(getStatusBar(), BorderLayout.SOUTH);
	}

	private void hookActionListeners() {
		new DirectoryChangeListener(getAddressBar(), getDirectoryExplorer(), getFileExplorer());
		// when table selection changed, update status bar
		this.getFileExplorer().getTable().getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent e) {
				File[] files = getFileExplorer().getSelectedFiles();
				if (null == files || 0 == files.length)
					return;
				Pair<Integer, Long> p = StatisticsUtilities.getFileStatistics(files);
				StringBuilder buf = new StringBuilder();
				buf.append(files.length).append(" objects selected ( ");
				buf.append(p.first()).append(" files, ");
				buf.append(files.length - p.first()).append(" folders )");
				getStatusBar().setInformation(buf.toString());
				buf.setLength(0);
				buf.append(SizeFormat.autoFormat(p.second())).append('B');
				getStatusBar().setSize(buf.toString());
			}
		});
	}

	private JToolBar getToolBar() {
		if (toolBar == null) {
			toolBar = new JToolBar();
			toolBar.add(getTreeButton());
			toolBar.add(getUpButton());
			toolBar.add(getRefreshButton());
			toolBar.addSeparator();
			toolBar.add(this.getAddressBar());
		}
		return toolBar;
	}

	private JSplitPane getSplitPane() {
		if (splitPane == null) {
			splitPane = ContainerFactory.createBorderlessSplitPane();
			splitPane.setName(getClass().getName() + ".splitPane");
			splitPane.setResizeWeight(0.3);
			splitPane.setDividerSize(5);
			splitPane.add(getDirectoryExplorer(), JSplitPane.LEFT);
			splitPane.add(getFileExplorer(), JSplitPane.RIGHT);
		}
		return splitPane;
	}

	private StatusBar getStatusBar() {
		if (null == statusBar)
			statusBar = new StatusBar();
		return statusBar;
	}

	class DirectoryChangeListener implements PropertyChangeListener {
		private AbstractExplorer[] explorers = null;

		DirectoryChangeListener(AbstractExplorer... explorers) {
			this.explorers = explorers;
			for (int i = 0; i < explorers.length; i++)
				explorers[i].addDirectoryChangeListener(this);
		}

		@Override
		public void propertyChange(PropertyChangeEvent e) {
			if (Explorer.DIRECTORY_CHANGED_PROPERTY.equals(e.getPropertyName())) {
				File directory = (File) e.getNewValue();
				Object source = e.getSource();
				for (int i = 0; i < explorers.length; i++)
					if (source != explorers[i])
						explorers[i].setDirectory(directory, false);
				Explorer.this.directory = directory;
				updateStatusBar();
			}
		}
	}
}
